package storager

import (
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"github.com/gabriel-vasile/mimetype"
	"github.com/gogf/gf/v2/net/ghttp"
	"io"
	"os"
	"path/filepath"
	"strings"
)

// 文件归属分类
const (
	KindImg   = "image" // 图片
	KindDoc   = "doc"   // 文档
	KindAudio = "audio" // 音频
	KindVideo = "video" // 视频
	KindZip   = "zip"   // 压缩包
	KindOther = "other" // 其他
)

var KindSlice = []string{KindImg, KindDoc, KindAudio, KindVideo, KindZip, KindOther}

var (
	// 图片类型
	imgType = map[string]string{
		".jpeg": "image/jpeg",
		".jpg":  "image/jpeg",
		".png":  "image/png",
		".gif":  "image/gif",
		".webp": "image/webp",
		".cr2":  "image/x-canon-cr2",
		".tif":  "image/tiff",
		".bmp":  "image/bmp",
		".heif": "image/heif",
		".jxr":  "image/vnd.ms-photo",
		".psd":  "image/vnd.adobe.photoshop",
		".ico":  "image/vnd.microsoft.icon",
		".dwg":  "image/vnd.dwg",
	}
	// 文档类型
	docType = map[string]string{
		".doc":      "application/msword",
		".docx":     "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
		".dot":      "application/msword",
		".xls":      "application/vnd.ms-excel",
		".xlt":      "application/vnd.ms-excel",
		".xlsx":     "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
		".xltx":     "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
		".ppt":      "application/vnd.ms-powerpoint",
		".pptx":     "application/vnd.openxmlformats-officedocument.presentationml.presentation",
		".pdf":      "application/pdf",
		".txt":      "text/plain",
		".csv":      "text/csv",
		".html":     "text/html",
		".xml":      "text/xml",
		".pptm":     "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
		".pot":      "application/vnd.ms-powerpoint",
		".wpd":      "application/wordperfect",
		".md":       "text/markdown",
		".json":     "application/json",
		".yaml":     "application/x-yaml",
		".markdown": "text/markdown",
		".asciidoc": "text/asciidoc",
		".xsl":      "application/xml",
		".wps":      "application/vnd.ms-works",
		".sxi":      "application/vnd.sun.xml.impress",
		".sti":      "application/vnd.sun.xml.impress.template",
		".odp":      "application/vnd.oasis.opendocument.presentation-template",
	}
	// 音频类型
	audioType = map[string]string{
		".mid":  "audio/midi",
		".mp3":  "audio/mpeg",
		".m4a":  "audio/mp4",
		".ogg":  "audio/ogg",
		".flac": "audio/x-flac",
		".wav":  "audio/x-wav",
		".amr":  "audio/amr",
		".aac":  "audio/aac",
		".aiff": "audio/x-aiff",
	}
	// 视频类型
	videoType = map[string]string{
		".mp4":  "video/mp4",
		".m4v":  "video/x-m4v",
		".mkv":  "video/x-matroska",
		".webm": "video/webm",
		".mov":  "video/quicktime",
		".avi":  "video/x-msvideo",
		".wmv":  "video/x-ms-wmv",
		".mpg":  "video/mpeg",
		".flv":  "video/x-flv",
		".3gp":  "video/3gpp",
	}
	// 压缩包
	zipType = map[string]string{
		".zip":    "application/zip",
		".rar":    "application/x-rar-compressed",
		".tar":    "application/x-tar",
		".gz":     "application/gzip",
		".7z":     "application/octet-stream",
		".tar.gz": "application/octet-stream",
	}
)

// FileInfoObject 文件信息
type FileInfoObject struct {
	Kind     string `json:"kind"`     // 文件上传类型
	MimeType string `json:"mimeType"` // 文件扩展类型
	Ext      string `json:"ext"`      // 文件扩展名
	Md5      string `json:"md5"`      // 文件md5
}

// IsImgType 判断是否为图片
func IsImgType(ext string) bool {
	_, ok := imgType[ext]
	return ok
}

// IsDocType 判断是否为文档
func IsDocType(ext string) bool {
	_, ok := docType[ext]
	return ok
}

// IsAudioType 判断是否为音频
func IsAudioType(ext string) bool {
	_, ok := audioType[ext]
	return ok
}

// IsVideoType 判断是否为视频
func IsVideoType(ext string) bool {
	_, ok := videoType[ext]
	return ok
}

// IsZipType 判断是否为压缩包
func IsZipType(ext string) bool {
	_, ok := zipType[ext]
	return ok
}

// ExtensionToKind 获取文件上传类型
func ExtensionToKind(ext string) string {
	if _, ok := imgType[ext]; ok {
		return KindImg
	} else if _, ok = audioType[ext]; ok {
		return KindAudio
	} else if _, ok = videoType[ext]; ok {
		return KindVideo
	} else if _, ok = zipType[ext]; ok {
		return KindZip
	} else if _, ok = docType[ext]; ok {
		return KindDoc
	}
	return KindOther
}

// Ext 获取文件后缀
func Ext(path string) string {
	ext := filepath.Ext(path)
	if p := strings.IndexByte(ext, '?'); p != -1 {
		ext = ext[0:p]
	}
	return strings.ToLower(ext)
}

func ExtName(path string) string {
	return strings.TrimLeft(Ext(path), ".")
}

// CalcFileMd5 计算文件md5值
func CalcFileMd5(filePath string) (string, error) {
	f, err := os.Open(filePath)
	if err != nil {
		return "", errors.New("os.Open failed")
	}
	defer func() {
		_ = f.Close()
	}()
	return FileReaderMd5(f)
}

// FileMimeType 获取文件的 mimetype、extension
func FileMimeType(filePath string) (mime string, ext string, err error) {
	f, err := os.Open(filePath)
	if err != nil {
		return "", "", errors.New("os.Open failed")
	}
	defer func() {
		_ = f.Close()
	}()
	mType, err := FileReaderMime(f)
	if err != nil {
		return "", "", err
	}
	return mType.String(), mType.Extension(), nil
}

// FileInfo 获取文件信息
func FileInfo(filePath string) (*FileInfoObject, error) {
	f, err := os.Open(filePath)
	if err != nil {
		return nil, errors.New("os.Open failed")
	}
	defer func() {
		_ = f.Close()
	}()
	mType, err := FileReaderMime(f)
	if err != nil {
		return nil, err
	}
	info := &FileInfoObject{
		MimeType: mType.String(),
		Ext:      mType.Extension(),
	}
	info.Kind = ExtensionToKind(info.Ext)
	info.Md5, err = FileReaderMd5(f)
	if err != nil {
		return nil, err
	}
	return info, nil
}

// FileReaderMd5 获取文件MD5
func FileReaderMd5(file io.Reader) (string, error) {
	h := md5.New()
	if _, err := io.Copy(h, file); err != nil {
		return "", errors.New("io.Copy failed")
	}
	s := ByteToString(h.Sum(nil))
	return s, nil
}

// FileReaderMime 获取文件mime
func FileReaderMime(file io.Reader) (*mimetype.MIME, error) {
	return mimetype.DetectReader(file)
}

// TypeToExtension 根据mimetype获取扩展名
func TypeToExtension(mimeType string) string {
	return mimetype.Lookup(mimeType).Extension()
}

// CalcUploadFileMd5 计算上传文件md5值
func CalcUploadFileMd5(file *ghttp.UploadFile) (string, error) {
	f, err := file.Open()
	if err != nil {
		return "", errors.New("file.Open failed")
	}
	defer func() {
		_ = f.Close()
	}()
	return FileReaderMd5(f)
}

func ByteToString(b []byte) string {
	return hex.EncodeToString(b)
}

func ByteToBase64(b []byte) string {
	return base64.StdEncoding.EncodeToString(b)
}
